﻿// HSS.Interlink.Web.BaseDownloadHandler.cs
// ----------------------------------------------------------------------------
// Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.html)
// ----------------------------------------------------------------------------
// HighSpeed-Solutions, LLC
// Copyright (c) 2001-2010
// ----------------------------------------------------------------------------
// File:       BaseDownloadHandler.cs
// Author:     HSS\gbanta
// Created:    07/16/2011
// Modified:   07/22/2011
// ----------------------------------------------------------------------------
namespace HSS.Interlink.Web
{
	#region Using Directives
	using System;
	using System.IO;
	using System.Web;
	using System.Net;
	#endregion

	#region BaseDownloadHandler
	/// <summary>
	/// BaseDownloadHandler is the base implementation of a File download
	/// processor and handles the Protocol portion of a download request.
	/// Implementors of this class should override the actual ProcessX methods.
	/// </summary>
	public abstract class BaseDownloadHandler : BaseHandler, IDownloadHandler
	{
		#region Constructors
		/// <summary>
		/// Constructor
		/// </summary>
		public BaseDownloadHandler()
		{

		}
		#endregion

		#region Properties
		/// <summary>
		/// Gets or sets the absolute path to the directory where Interlink stores temporary files.
		/// </summary>
		/// <remarks>
		/// <para>The default path; ~\temp\downloadtemp.</para>
		/// </remarks>
		public string TempFolder
		{
			get
			{
				if (!_tempFolderSet)
				{
					_tempFolderSet = true;
					_tempFolder = InterlinkUtilities.GetAppRelativeTempFolder("downloadtemp");
				}
				return _tempFolder;
			}
			set
			{
				_tempFolderSet = true;
				_tempFolder = value;
			}
		} string _tempFolder; bool _tempFolderSet;
		/// <summary>
		/// Gets the array of one or more file names or ids or keys being requested for download.
		/// </summary>
		public string[] RequestedFiles
		{
			get;
			private set;
		}
		/// <summary>
		/// Gets the file names or id or key being requested for download.
		/// </summary>
		public string RequestedFile
		{
			get
			{
				return RequestedFiles[0];
			}
		}
		#endregion

		#region Methods

		/// <summary>
		/// Processes the actual download request. This method is used by HSS.Interlink and not meant to be called from user code.
		/// </summary>
		/// <param name="context">The current HttpContext</param>
		/// <param name="fileQuery">The currently parsed QueryString</param>
		void IDownloadHandler.Download(HttpContext context, FileQuery fileQuery)
		{
			string jobFile = string.Empty;

			try
			{
				if (!this.CommonInit(context, fileQuery))
				{
					this.OnError(new WebException("Missing query string and is required."));
					return;
				}

				#region Process Open requests separate from download requests
				if (this.Query.Command == FileQueryCommand.OpenFileInline)
				{
					context.Response.Clear();
					context.Response.StatusCode = 403;
					return;
				}
				#endregion

				jobFile = Path.Combine(this.TempFolder, this.Query.JobId.ToString());

				switch (context.Request.HttpMethod.ToUpper())
				{
					case "POST":
						#region POST commands
						switch (this.Query.Command)
						{
							case FileQueryCommand.Prepare:
								context.Request.SaveAs(jobFile, false);
								context.Request.InputStream.Close();
								context.Response.Clear();
								return;

							default:
								context.Response.Clear();
								context.Response.StatusCode = 403;
								return;
						}
						#endregion

					case "GET":
						#region GET commands
						switch (this.Query.Command)
						{
							case FileQueryCommand.GetSingleFile:
								#region Download SingleFile
								if (!this.SetRequestedFiles(jobFile, true))
									return;
								if (!this.IsAuthorized(jobFile))
									return;
								if (!this.GetSingleFile())
								{
									if (File.Exists(jobFile))
										File.Delete(jobFile);
									context.Response.Clear();
									context.Response.StatusCode = 403;
								}
								return;
								#endregion

							case FileQueryCommand.GetMultipleFiles:
								#region Download MultipleFiles
								if (!this.SetRequestedFiles(jobFile, false))
									return;
								if (!this.IsAuthorized(jobFile))
									return;
								if (!this.GetMultipleFiles())
								{
									if (File.Exists(jobFile))
										File.Delete(jobFile);
									context.Response.Clear();
									context.Response.StatusCode = 403;
								}
								return;
								#endregion

							default:
								#region No other commands
								context.Request.InputStream.Close();
								context.Response.Clear();
								context.Response.StatusCode = 403;
								return;
								#endregion
						}
						#endregion

					default:
						#region No other VERBS supported
						if (File.Exists(jobFile))
							File.Delete(jobFile);
						context.Request.InputStream.Close();
						context.Response.Clear();
						context.Response.StatusCode = 405; // Method Not Allowed
						return;
						#endregion
				}
			}
			catch (Exception ex)
			{
				int responseCode = OnError(ex);

				#region Handler Exceptions
				try
				{
					if (File.Exists(jobFile))
						File.Delete(jobFile);
				}
				catch { }
				try
				{
					context.Request.InputStream.Close();
					context.Response.Clear();
				}
				catch { }
				try
				{
					context.Response.StatusCode = responseCode;
				}
				catch { }
				#endregion
			}
			finally
			{
				this.PurgeTempFiles();
			}
		}
		/// <summary>
		/// Perform the open inline request. This method is used by HSS.Interlink and not meant to be called from user code.
		/// </summary>
		/// <param name="context">The current HttpContext</param>
		/// <param name="fileQuery">The currently parsed QueryString</param>
		void IDownloadHandler.OpenInline(System.Web.HttpContext context, FileQuery fileQuery)
		{
			try
			{
				if (!this.CommonInit(context, fileQuery))
				{
					this.OnError(new WebException("Missing query string and is required."));
					return;
				}

				if (this.Query.Command == FileQueryCommand.OpenFileInline)
				{
					this.RequestedFiles = new string[] { this.Query.FileName };
					if (!IsAuthorized(string.Empty))
						return;
					if (this.OpenFileInline())
						return;
				}

				context.Response.Clear();
				context.Response.StatusCode = 403;

			}
			catch (Exception ex)
			{
				int responseCode = OnError(ex);

				#region Handler Exceptions
				context.Request.InputStream.Close();
				context.Response.Clear();
				context.Response.StatusCode = responseCode;
				#endregion
			}
		}
		/// <summary>
		/// Perform the open as attachment request. This method is used by HSS.Interlink and not meant to be called from user code.
		/// </summary>
		/// <param name="context">The current HttpContext</param>
		/// <param name="fileQuery">The currently parsed QueryString</param>
		void IDownloadHandler.OpenAsAttachment(System.Web.HttpContext context, FileQuery fileQuery)
		{
			try
			{
				if (!this.CommonInit(context, fileQuery))
				{
					this.OnError(new WebException("Missing query string and is required."));
					return;
				}

				if (this.Query.Command == FileQueryCommand.OpenFileAsAttachment)
				{
					this.RequestedFiles = new string[] { this.Query.FileName };
					if (!IsAuthorized(string.Empty))
						return;
					if (this.OpenFileAsAttachment())
						return;
				}

				context.Response.Clear();
				context.Response.StatusCode = 403;

			}
			catch (Exception ex)
			{
				int responseCode = OnError(ex);

				#region Handler Exceptions
				context.Request.InputStream.Close();
				context.Response.Clear();
				context.Response.StatusCode = responseCode;
				#endregion
			}
		}

		/// <summary>
		/// Gets a temporary file name based on the file extension provided by
		/// combining with a Random GUID.
		/// </summary>
		/// <param name="fileExte">The file extension of the temp file.</param>
		/// <returns>The full path to the temp file by combining the <c>DownloadTempFolder</c> with the file name.</returns>
		protected virtual string GetTempFile(string fileExte)
		{
			return Path.Combine(this.TempFolder, Guid.NewGuid().ToString() + fileExte);
		}

		#region Private
		private void PurgeTempFiles()
		{
			this.PurgeTempFiles(this.TempFolder);
		}
		private bool SetRequestedFiles(string jobFile, bool single)
		{
			this.RequestedFiles = new string[] { };

			if (!File.Exists(jobFile))
			{
				this.Context.Request.InputStream.Close();
				this.Context.Response.Clear();
				this.Context.Response.StatusCode = 500;
				return false;
			}

			var requestContents = File.ReadAllText(jobFile);
			File.SetLastAccessTime(jobFile, DateTime.Now);

			var files = requestContents.Split(',');

			if (files.Length > 0 &&
				((single && files.Length == 1) || (!single && files.Length > 1)))
			{
				this.RequestedFiles = files;
				this.Context.Request.InputStream.Close();
				return true;
			}

			if (File.Exists(jobFile))
				File.Delete(jobFile);
			this.Context.Request.InputStream.Close();
			this.Context.Response.Clear();
			this.Context.Response.StatusCode = 403;

			return false;
		}
		#endregion

		#region Abstract
		/// <summary>
		/// Process an OpenFile request with a Content-Disposition of 'Inline'.
		/// </summary>
		/// <returns>true if successful; otherwise false</returns>
		public abstract bool OpenFileInline();
		/// <summary>
		/// Process an OpenFile request with a Content-Disposition of 'Attachment'.
		/// </summary>
		/// <returns>true if successful; otherwise false</returns>
		public abstract bool OpenFileAsAttachment();
		/// <summary>
		/// Process a single file download request.
		/// </summary>
		/// <returns>true if successful; otherwise false</returns>
		public abstract bool GetSingleFile();
		/// <summary>
		/// Process a multiple file download request.
		/// </summary>
		/// <returns>true if successful; otherwise false</returns>
		public abstract bool GetMultipleFiles();
		/// <summary>
		/// Called when an error is encountered.
		/// </summary>
		/// <param name="ex">The exception that was encountered.</param>
		/// <returns>The HTTP Response Status to return (ex. 403).</returns>
		public abstract int OnError(Exception ex);
		#endregion

		#endregion
	}
	#endregion
}